/*
 * Copyright (C) 2017 Intel Corporation.  All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 */

// Copyright (c) WanSheng Intelligent Corp. All rights reserved.


#define LOG_TAG "misc"

#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/stat.h>
#include <errno.h>
#include <signal.h>
#include <pthread.h>
#include <assert.h>
#include <dirent.h>
#include <fcntl.h>
#include <time.h>
#include <sys/wait.h>


#include "iniparser.h"
#include "logs.h"
#include "misc.h"
#include "wasdk_internal_consts.h"


#define PATH_LEN 256


#define LOCKMODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)


void *malloc_z (size_t __size)
{
    void * r = malloc(__size);
    if (r == NULL) return NULL;

    memset(r, 0, __size);
    return r;
}

// return true if this function malloc a new buffer
char * get_string(char * data, int data_len, bool *allocated)
{
    *allocated = false;
    for(int i=data_len-1;i>=0;i--)
    {
        if(data[i] == 0)
            return data;
    }


    char *str_out = (char*) malloc(data_len+1);
    memcpy(str_out, data, data_len);
    str_out[data_len] = 0;
    *allocated = 1;
    return str_out;
}

char * make_string(char * data, int data_len)
{
    char *str_out = (char*) malloc(data_len+1);
    if(str_out == NULL)
        return NULL;
    memcpy(str_out, data, data_len);
    str_out[data_len] = 0;

    return str_out;
}


int lockfile(int fd)
{
    struct flock fl;

    fl.l_type = F_WRLCK;  /* write lock */
    fl.l_start = 0;
    fl.l_whence = SEEK_SET;
    fl.l_len = 0;  //lock the whole file

    return(fcntl(fd, F_SETLK, &fl));
}

int already_running(const char *filename)
{
    int fd;
    char buf[16];

    fd = open(filename, O_RDWR | O_CREAT, LOCKMODE);
    if (fd < 0)
    {
        WARNING2("can't open %s: %m\n", filename);
        return (1);
    }
    if (lockfile(fd) == -1)
    {
        if (errno == EACCES || errno == EAGAIN)
        {
            WARNING2("file: %s already locked\n", filename);
            close(fd);
            return 1;
        }
        WARNING2("can't lock %s: %m\n", filename);
        return (1);
    }
    int ret = ftruncate(fd, 0);
    sprintf(buf, "%ld", (long)getpid());
    ret |= write(fd, buf, strlen(buf) + 1);

    if(ret != 0)
    {
        WARNING2("already_running: rt=%d", ret);
    }

    return 0;
}

bool test_file_lock(const char *filename)
{
    int fd;
    bool locked = false;

    if(access(filename, F_OK) != 0)
        return false;

    fd = open(filename, O_RDWR | O_CREAT, LOCKMODE);
    if (fd < 0)
    {
        //printf("test_file_lock: open error %d. %s\n", errno, filename);
        if (errno == EACCES || errno == EAGAIN)
        {
            return true;
        }
    }

    if (lockfile(fd) == -1)
    {
        //printf("test_file_lock: lock error %d. %s\n", errno, filename);
        locked = true;
    }
    close(fd);
    return locked;
}

int find_char_and_copy(char * source, int source_len, char c, char * dest, int dest_len)
{

    int cnt = 0;
    char * p = source;
    if(source_len == SOURCE_LEN_STRING)
        source_len = strlen(source);
    while(cnt < source_len && *p != c)
    {
        cnt ++;
        p++;
    }

    if(cnt == source_len)
        return 0;

    if(cnt >= dest_len)
        cnt = dest_len -1;
    memcpy(dest, source, cnt);
    dest[cnt] = 0;

    return cnt;
}


void prv_output_buffer(char * buffer,
        int length)
{
    bh_output_buffer(stderr, buffer, length);
}

#define ARRAY_SIZE 16
void bh_output_buffer(FILE * stream, char * buffer,
        int length)
{
    int i;
    uint8_t array[ARRAY_SIZE];

    i = 0;
    while (i < length)
    {
        int j;
        fprintf(stream, "     ");

        memcpy(array, buffer+i, (length-i) < ARRAY_SIZE?(length-i):16);

        for (j = 0 ; j < ARRAY_SIZE && i+j < length; j++)
        {
            fprintf(stream, "%02X ", array[j]);
        }
        while (j < ARRAY_SIZE)
        {
            fprintf(stream, "   ");
            j++;
        }
        fprintf(stream, "  ");
        for (j = 0 ; j < ARRAY_SIZE && i+j < length; j++)
        {
            if (isprint(array[j]))
                fprintf(stream, "%c ", array[j]);
            else
                fprintf(stream, ". ");
        }
        fprintf(stream, "\n");

        i += ARRAY_SIZE;
    }
}



// use the mkdir -p to create all parent folders
void make_full_dir(char * path)
{
    char cmd[512];
    DIR *dirptr = NULL;

    if ((dirptr = opendir(path)) == NULL)
    {
        snprintf(cmd,sizeof(cmd),"mkdir -p %s",path);
        int ret = system(cmd);
        (void)ret;
    }
    else
    {
        closedir(dirptr);
    }
}


bool wa_is_dir(char * path)
{
    DIR *dirptr = NULL;

    if ((dirptr = opendir(path)) == NULL)
    {
        return false;
    }
    else
    {
        closedir(dirptr);
        return true;
    }
}


#include <sys/stat.h>

unsigned long get_file_size(const char *path , time_t * modi_time)
{
    unsigned long filesize = -1;
    struct stat statbuff;
    if(stat(path, &statbuff) < 0){
        TraceD(LOG_FLAG_MISC, "Fail to get file stats. err:%s, file: %s",
                strerror (errno),path);
        return filesize;
    }else{
        filesize = statbuff.st_size;

        if(modi_time) *modi_time = statbuff.st_mtime;
    }
    return filesize;
}




unsigned long delete_folder_big_files(char * path, unsigned int bytes)
{
    unsigned long total = 0;
    DIR           *d;
    struct dirent *dir;
    d = opendir(path);
    if (d)
    {
        while ((dir = readdir(d)) != NULL )
        {
            if(dir->d_type != DT_REG)
                continue;

            time_t modi_time;
            char filename[512];
            strcpy(filename, path);
            strcat(filename, dir->d_name);

            unsigned long filesize = get_file_size(filename, &modi_time);
            if(filesize == (unsigned long)-1)
                continue;

            if(filesize > bytes)
            {
                char cmd[1024];
                snprintf(cmd, sizeof(cmd), "rm -r %s", filename);
                int ret = system(cmd);
                (void)ret;
                total += filesize;
            }


        }
        closedir(d);
    }

    return total;
}


// delete_old_logs: delete any closed log which is old for "days" or bigger than "size_threshold"
// size_threshold: -1 mean no checking on this
int delete_old_or_big_files(char * closed_dir, int days, unsigned long size_threshold)
{
    int cnt = 0;
    time_t now = time(NULL);
    DIR           *d;
    struct dirent *dir;
    d = opendir(closed_dir);
    if (d)
    {
        while ((dir = readdir(d)) != NULL)
        {
            if(dir->d_type != DT_REG)
                continue;

            time_t modi_time;
            char filename[512];
            strcpy(filename, closed_dir);
            strcat(filename, dir->d_name);

            unsigned long filesize = get_file_size(filename, &modi_time);
            if(filesize == (unsigned long ) -1)
                continue;

            if((modi_time > now || SECONDS_TO_DAYS(now - modi_time) >= days)||
                    (size_threshold != (unsigned long )-1 && filesize > size_threshold))
            {
                int ret = remove(filename);
                if(ret!= 0)
                {
                    TraceD(LOG_FLAG_MISC, "Failed to remove old log file [%s], error: %d", dir->d_name, errno);
                }
                else
                {
                    char str[32] = {0};
                    struct tm *tmp;

                    cnt ++;

                    tmp = localtime(&modi_time);
                    if(tmp)
                    {
                        strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S", tmp);
                    }
                    TraceD(LOG_FLAG_MISC,"Removed old log file [%s], modify time: %s, size: %d K", dir->d_name, str, filesize/1024);
                }
            }

        }
        closedir(d);
    }

    return cnt;
}


unsigned long dir_files_size(char * path)
{
    unsigned long total = 0;
    DIR           *d;
    struct dirent *dir;
    d = opendir(path);
    if (d)
    {
        while ((dir = readdir(d)) != NULL )
        {
            if(dir->d_type != DT_REG)
                continue;

            time_t modi_time;
            char filename[512];
            strcpy(filename, path);
            strcat(filename, dir->d_name);

            unsigned long filesize = get_file_size(filename, &modi_time);
            if(filesize ==  (unsigned long)-1)
                continue;

            total += filesize;
        }
        closedir(d);
    }

    return total;
}



int ini_get_int(const char* cfgname, char * key, int def)
{
    dictionary  *   ini;
    int val=def;

    ini = iniparser_load(cfgname);


    if(NULL == ini)
    {
        WARNING("Unable to load log config %s\n", cfgname);
    }
    else
    {
        val = iniparser_getint(ini, key, def);
        iniparser_freedict(ini);
    }

    return val;
}

const char * ini_get_str(const char* cfgname, const char * key, const char * def)
{
    dictionary  *   ini;
    const char * val=def;

    if(access(cfgname, F_OK) != 0)
        return NULL;

    ini = iniparser_load(cfgname);
    if(NULL == ini)
    {
        TraceD(LOG_FLAG_MISC,"ini_get_str: Unable to load log config %s\n", cfgname);
    }
    else
    {
        val = iniparser_getstring(ini, key, def);
        iniparser_freedict(ini);
    }
    return val;
}


// value=NULL: unset the key from the file
bool set_ini_key(char *cfgname , const char * key, const char * value)
{
    dictionary  *   ini;
    bool ret = false;
    char tmp_name[512];

    if(access(cfgname, F_OK) != 0)
        return NULL;

    ini = iniparser_load(cfgname);
    if(NULL == ini)
    {
        // if the file exist, we should not continue to overwrite it.
        int result = access(cfgname,R_OK|W_OK );
        if(result == 0)
        {
            TraceD(LOG_FLAG_MISC,"set_ini_key: Unable load %s, but file present", cfgname);
            return false;
        }
        else
        {
            ini = dictionary_new(0);
            if(NULL == ini)
            {
                return false;
            }
        }
    }

    if(value)
    {
        // add the section first
        char * str = strdup(key);
        if(!str)return false;

        char * p = strchr(str, ':');
        if(p)
        {
            *p = 0;
            iniparser_set(ini,str,  NULL);
            *p = ':';
        }
        iniparser_set(ini,key,  value);
        free(str);

    }
    else
    {
        iniparser_unset(ini, key);
    }

    strcpy(tmp_name, cfgname);
    strcat(tmp_name, ".tmp");
    remove (tmp_name);
    FILE * fp = fopen(tmp_name, "w+");
    if(NULL != fp){
        iniparser_dump_ini(ini, fp);
        fclose(fp);

        int result = rename(tmp_name, cfgname);
        if(result != 0){
            WARNING("set_ini_key: failed to rename %s, err:%s", cfgname, strerror(errno));
        }
        else {
            ret = true;
        }
    }
    else
    {
        WARNING("set_ini_key: failed to CREATE %s, err:%s",
                tmp_name, strerror(errno));
    }

    iniparser_freedict(ini);

    return ret;
}




int load_file_to_memory(const char *filename, char **result)
{
    size_t size = 0;
    FILE *f = fopen(filename, "rb");
    if (f == NULL)
    {
        TraceD(LOG_FLAG_MISC, "load_file_to_memory: open fail. error: %d, file: %s", errno, filename);
        *result = NULL;
        return -1; // -1 means file opening fail
    }
    fseek(f, 0, SEEK_END);
    size = ftell(f);
    fseek(f, 0, SEEK_SET);
    *result = (char *)malloc(size+1);
    if (size != fread(*result, sizeof(char), size, f))
    {
        TraceD(LOG_FLAG_MISC,"load_file_to_memory: read fail. error: %d, file: %s", errno, filename);
        free(*result);
        return -2; // -2 means file reading fail
    }
    fclose(f);
    (*result)[size] = 0;
    return size;
}


bool check_mount_path(char * path)
{
    char * content = NULL;
    char cmd[256];

    // the file under /proc can not be read normally
    // https://stackoverflow.com/questions/5713451/is-it-safe-to-parse-a-proc-file
    snprintf(cmd, sizeof(cmd), "cat /proc/mounts > /mounts.tmp");
    int ret = system(cmd);
    (void)ret;
    int size = load_file_to_memory("/mounts.tmp", &content);
    remove("/mounts.tmp");

    if(content == NULL)
    {
        TraceD(LOG_FLAG_MISC, "load /proc/mounts failed");
        return false;
    }

    TraceD(LOG_FLAG_MISC, "content (size=%d): \n%s", size,  content);
    char * found = strstr(content, path);
    free(content);
    return (found != NULL);
}

/*
@function:   use the install command to copy the files with user given mode and owner.
             if the install command not exist, use the cp command instead.

@notes:     not pass the test for directory install yet.             
*/
void do_install(const char *source, const char *dest, const char * mode, const char * owner, const char * group)
{
    if(access("/usr/bin/install", F_OK) != 0)
    {
        TraceD(LOG_FLAG_MISC, "Find install command fail. src=%s, dest=%s, /usr/bin/install not exist", source, dest);
        do_copy((char *)source, (char *)dest, (char *)"-rp");
        return;
    }

    char cmd[512];
    char mode_args[100];
    char owner_args[100];
    char group_args[100];
    if(mode)
    {
        sprintf(mode_args, "-m %s", mode);
    }
    else
    {
        mode_args[0] = 0;
    }
    if(owner)
    {
        sprintf(owner_args, "-o %s", owner);
    }
    else
    {
        owner_args[0] = 0;
    }
    if(group)
    {
        sprintf(group_args, "-g %s", group);
    }
    else
    {
        group_args[0] = 0;
    }
    sprintf(cmd, "/usr/bin/install %s %s %s %s %s", mode_args, owner_args, group_args, source, dest);
    WARNING2("do_install: %s", cmd);
    int ret = system(cmd);
    (void)ret;
}

void do_copy(char *source, char *dest, char * args)
{
    int childExitStatus;
    pid_t pid;
    int status;
    if (!source || !dest) {
        /* handle as you wish */
    }

    pid = fork();

    if (pid == 0) { /* child */
        execl("/bin/cp", "/bin/cp", source, dest, args, (char *)0);
    }
    else if (pid < 0)
    {

        TraceD(LOG_FLAG_MISC, "copy fail. src=%s, dest=%s, failed to run the cp command", source, dest);
        /* error - couldn't start process - you decide how to handle */
    }
    else {
        /* parent - wait for child - this has all error handling, you
         * could just call wait() as long as you are only expecting to
         * have one child process at a time.
         */
        pid_t ws = waitpid( pid, &childExitStatus,0);//WNOHANG
        if (ws == -1)
        { /* error - handle as you wish */
            TraceD(LOG_FLAG_MISC, "copy fail. src=%s, dest=%s, parent process waitpid returned %d", source,dest, ws);
        }

        if( WIFEXITED(childExitStatus)) /* exit code in childExitStatus */
        {
            status = WEXITSTATUS(childExitStatus); /* zero is normal exit */
            TraceI(LOG_FLAG_MISC,"copy src=%s, dest=%s done, ret code:%d",source,dest,status);
            /* handle non-zero as you wish */
        }
        else if (WIFSIGNALED(childExitStatus)) /* killed */
        {
            WARNING2("copy: parent process WIFSIGNALED");
        }
        else if (WIFSTOPPED(childExitStatus)) /* stopped */
        {
            WARNING2("copy: parent process WIFSTOPPED");
        }
    }
}


int rm_dir(char * path)
{
    int pid = fork();


    if( strchr(path, '*') != NULL ||
        strcmp(path, "/usr") == 0 ||
        strcmp(path, "/") == 0 ||
        strstr(path, "..") != NULL)
    {
        ERROR("rm_dir: reject to delete: %s", path);
        return -1;
    }

    if(pid > 0) {
         int status;
         waitpid(pid, &status, 0);

         if(!WIFEXITED(status))
         {
             WALOG("rm_dir: del %s failed.", path);
             return -1; //error
         }

         WALOG("rm_dir: del %s returned %d.", path, status);

         return WEXITSTATUS(status); //0 is no error
    }
    else if(pid == 0) {
       close(0); //no stdio
       close(1);
       close(2);

       execlp("rm", "rm", "-r", path, NULL);
    }
    else {
        WALOG("rm_dir: del %s failed. fork error", path);
       return -1; //fork failed
    }

    return 0;
}



int save_content(char * filepath, char * content, int len)
{
    char tmp[PATH_LEN];
    char tmp2[PATH_LEN];
    FILE* destFile;

    sprintf(tmp2, "%s_%lu", filepath, time(NULL));
    strcpy(tmp, tmp2);
    strcat(tmp, ".tmp");
    destFile = fopen(tmp, "wb");
    if(destFile==NULL)
    {
        ERROR("save_content:Could not open file [%s]. err=%d", tmp, errno);
        return -1;
    }

    fwrite(content, 1, len, destFile);
    fflush(destFile);
    fclose(destFile);

    if(0 == access(filepath, 0))
    {
        remove(tmp2);
        rename(filepath, tmp2);
    }

    rename(tmp, filepath);

    // if rename failed, then try to recover the old file
    if(0 != access(filepath, 0))
    {
        ERROR("rename failed (%d),recover old from %s to %s", errno, tmp2,filepath);
        rename(tmp2, filepath);
        remove(tmp);
        return -3;
    }
    else
    {
        TraceI(LOG_FLAG_MISC,"save_content: success saved [%s], %d bytes",filepath, len);
    }
    remove(tmp);
    remove(tmp2);
    return 0;
}


int wa_create_socket(int port, int addressFamily)
{
    int s = -1;
    struct addrinfo hints;
    struct addrinfo *res;
    struct addrinfo *p;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = addressFamily;
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_flags = AI_PASSIVE;

    char portStr[100];
    snprintf(portStr, sizeof(portStr), "%d", port);

    if (0 != getaddrinfo(NULL, (const char*) portStr, &hints, &res))
    {
        return -1;
    }

    for(p = res ; p != NULL && s == -1 ; p = p->ai_next)
    {
        s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
        if (s >= 0)
        {
            if (-1 == bind(s, p->ai_addr, p->ai_addrlen))
            {
                close(s);
                s = -1;
            }
        }
    }

    freeaddrinfo(res);

    return s;
}

int new_random_socket(bool is_udp, struct sockaddr * addr_out, socklen_t *socklen_inout)
{
    struct sockaddr_in addr;
    socklen_t socklen;

    int sock_fd = socket(AF_INET, is_udp?SOCK_DGRAM:SOCK_STREAM, 0);

    addr.sin_family = AF_INET;
    addr.sin_port = 0; // bind random port
    addr.sin_addr.s_addr = INADDR_ANY;

    socklen = sizeof(addr);
    if (bind(sock_fd, (struct sockaddr *)&addr, socklen)) {
        perror("bind error");
        return -1;
    }

    getsockname(sock_fd, (struct sockaddr *)&addr, &socklen);
    if(addr_out && socklen_inout && *socklen_inout >= socklen)
    {
        memcpy(addr_out, &addr, socklen);
        *socklen_inout = socklen;
    }
    return sock_fd;
}


// create a passive udp socket, no udp port specified
int create_socket_random(int * port)
{
    int s = -1;
    struct addrinfo hints;
    struct addrinfo *res;
    struct addrinfo *p;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_DGRAM;
    hints.ai_flags = AI_PASSIVE;

    if (0 != getaddrinfo("127.0.0.1", "", &hints, &res))
    {
        return -1;
    }

    for(p = res ; p != NULL && s == -1 ; p = p->ai_next)
    {
        s = socket(p->ai_family, p->ai_socktype | SOCK_CLOEXEC, p->ai_protocol);
        if (s >= 0)
        {
            if (-1 == bind(s, p->ai_addr, p->ai_addrlen))
            {
                close(s);
                s = -1;
            }

            struct sockaddr_in *ai_addr = (struct sockaddr_in *) p->ai_addr;
            if(port) *port = ai_addr->sin_port;
            break;
        }
    }

    freeaddrinfo(res);

    return s;
}


int create_socket_r()
{
    return create_socket_random(NULL);
}

/*
 * get the value of the key from following format buffer:
 *  key1=value1;key2=value2;key3=value3
 */
char * find_key_value(char * buffer, int buffer_len,
        const char * key,
        char * value, int value_len,
        char  delimiter)
{
    char * p = buffer;
    int remaining = buffer_len;
    int key_len = strlen(key);

    while(*p != 0 && remaining > 0)
    {
        while((*p == ' '||*p == '\t') || *p == delimiter)
        {
            p++;
            remaining --;
        }

        if(remaining <= key_len)
            return NULL;

        // find the key
        if( 0 == strncmp(p, key, key_len))
        {
            p += (key_len);
            remaining -= (key_len);
            while((*p == ' '||*p == '\t') && remaining > 0)
            {
                p++;
                remaining --;
            }
            if(remaining == 0) return NULL;

            //
            if(*p == '='){
                p++;
                remaining --;
                while((*p == ' '||*p == '\t') && remaining > 0)
                {
                    p++;
                    remaining --;
                }
                char * v  = value;
                memset(value, 0, value_len);
                value_len --;	// ensure last char is 0
                while(*p != delimiter && remaining >0 && value_len>0)
                {
                    *v ++ = *p ++;
                    remaining --;
                    value_len --;
                }
                return value;
            }
        }

        // goto next key
        while(*p != delimiter && remaining >0)
        {
            p ++;
            remaining --;
        }
    }

    return NULL;
}

// return: value
// head: the moving cursor
//
 char * travel_key_value(char * buffer, int buffer_len,
        unsigned int *head,
        char * key_buf, int key_buf_len,
        int *value_len,
        char  delimiter)
{

    if(*head >= (unsigned int)buffer_len)
        return NULL;

    int remaining = buffer_len - *head;
    buffer += *head;
    char * p = buffer;

    while(remaining > 0 && (*p == ' ' || *p == delimiter))
    {
        p++;
        remaining --;
    }

    if(remaining == 0)
        return NULL;

    char * key = p;
    while(remaining > 0 && (*p != delimiter))
    {
        p++;
        remaining --;
    }
    if(remaining == 0)
        return NULL;

    if((p-key) >= key_buf_len)
        return NULL;
    int len = (int)(p-key);
    strncpy(key_buf, key, len);
    key_buf[len] = 0;

    p++;
    remaining --;

    while(remaining > 0 && (*p == ' ' || *p == delimiter))
    {
        p++;
        remaining --;
    }

    if(remaining == 0)
    {
        value_len = 0;
        *head = (buffer_len - remaining);
        return (char *)"";
    }

    char * item_start = p;
    while(remaining > 0 && *p != delimiter )
    {
        remaining --;
        value_len --;
    }

    *value_len = (int)(p - item_start);

    *head = (buffer_len - remaining);

    return item_start;
}


int  float_to_str(double value, int decimals, char * str, int len)
{
    char buf[100];
    sprintf(buf, "%.*lf", decimals, value);

    if(strchr(buf, '.') != NULL)
    {
        int n  = strlen(buf);
        for(int i=n-1;i>0;i--)
        {
            if(buf[i] == '.')
            {
                buf[i] = 0;
                break;
            }
            if(buf[i] == '0')
            {
                buf[i] = 0;
            }
            else
            {
                break;
            }
        }
    }
    strncpy(str, buf, len-1);
    return strlen(str);
}

char * read_ini_key(const char * ini_path, const char * key, char * buf, int buf_len)
{
    const char * path = ini_path;
    char line[256];
    if(access(path, R_OK) != 0)
    {
        return NULL;
    }
    /* Open file */
    FILE *file = fopen(path, "r");
    
    if (!file)
    {
        return NULL;
    }
    
    /* Get each line until there are none left */
    while (fgets(line, sizeof(line), file))
    {
        if(find_key_value(line, strlen(line), key, buf, buf_len, '\n'))
        {
            fclose(file);
            return buf;
        }

    }
    
    /* Close file */
    fclose(file);
    return NULL;
}



char * read_global_setting(const char * key, char * buf, int buf_len)
{
    return read_ini_key("/etc/ams/wasome.cfg", key, buf, buf_len);
}

